package it.unibo.refolding.pico;

import it.unibo.refolding.common.CommonThings;

import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import com.google.common.base.Preconditions;

public class LoopParams {
  
  private static final Logger logger = Logger.getLogger(LoopParams.class.getSimpleName());
  
  public final double approachRampSizeM, approachVelocityMPerS, surfaceDelayS, retractionRampSizeM,
    retractionVelocityMPerS, rectificationTimeS;
  public final int iterations;
  public final boolean extensiveRetraction;

  private static final Pattern pattern = Pattern.compile(String.format(
    Locale.ROOT,
    "ApproachRampSize(%1$s)nm_ApproachVelocity(%1$s)nmPerS_Hover(%1$s)ms_RetractionRampSize(%1$s)nm_RetractionVelocity(%1$s)nmPerS_RectificationTime(%1$s)s_Iterations(%2$s)_ExtensiveRetraction(%3$s)",
    CommonThings.FP_REGEX, CommonThings.I_REGEX, CommonThings.B_REGEX));

  /**
   * @param rectificationTimeS only makes sense when iterations != 1
   */
  public LoopParams(double approachRampSizeM, double approachVelocityMPerS, double surfaceDelayS,
    double retractionRampSizeM, double retractionVelocityMPerS, double rectificationTimeS, int iterations,
    boolean extensiveRetraction) {
    this.approachRampSizeM = approachRampSizeM;
    this.approachVelocityMPerS = approachVelocityMPerS;
    this.surfaceDelayS = surfaceDelayS;
    this.retractionRampSizeM = retractionRampSizeM;
    this.retractionVelocityMPerS = retractionVelocityMPerS;
    this.rectificationTimeS = rectificationTimeS;
    this.iterations = iterations;
    this.extensiveRetraction = extensiveRetraction;
  }

  public String toExactString() {
    return String.format(
      Locale.ROOT,
      "ApproachRampSize%snm_ApproachVelocity%snmPerS_Hover%sms_RetractionRampSize%snm_RetractionVelocity%snmPerS_RectificationTime%ss_Iterations%d_ExtensiveRetraction%s",
      Double.toString(approachRampSizeM * 1e9), Double.toString(approachVelocityMPerS * 1e9), Double.toString(surfaceDelayS),
      Double.toString(retractionRampSizeM * 1e9), Double.toString(retractionVelocityMPerS * 1e9), Double.toString(rectificationTimeS),
      iterations, extensiveRetraction
      );
  }
  
  @Override
  public String toString() {
    return toExactString();
  }

  public static LoopParams parse(String s) {
    Matcher matcher = pattern.matcher(s);
    boolean matches = matcher.matches();
    if (matches) {
      int i = 0;
      double approachRampSizeM = Double.parseDouble(matcher.group(++i)) / 1e9;
      double approachVelocityMPerS = Double.parseDouble(matcher.group(++i)) / 1e9;
      double surfaceDelayS = Double.parseDouble(matcher.group(++i));
      double retractionRampSizeM = Double.parseDouble(matcher.group(++i)) / 1e9;
      double retractionVelocityMPerS = Double.parseDouble(matcher.group(++i)) / 1e9;
      double rectificationTimeS = Double.parseDouble(matcher.group(++i));
      int iterations = Integer.parseInt(matcher.group(++i));
      boolean extensiveRetraction = CommonThings.parseBoolean(matcher.group(++i));
      return new LoopParams(
        approachRampSizeM, approachVelocityMPerS, surfaceDelayS, retractionRampSizeM,
        retractionVelocityMPerS, rectificationTimeS, iterations, extensiveRetraction);
    } else {
      logger.log(Level.WARNING, "{0} does not match {1}.", new Object[] {s, pattern});
      return null;
    }
  }
  
  public static LoopParams parseOrFail(String s) {
    LoopParams x = parse(s);
    Preconditions.checkNotNull(x, "Could not parse %s.", s);
    return x;
  }
  
  @Override
  public int hashCode() {
    return HashCodeBuilder.reflectionHashCode(this);
  }
  
  @Override
  public boolean equals(Object other) {
    return EqualsBuilder.reflectionEquals(this, other);
  }
}
